#!/usr/bin/python3
# -*- coding:utf-8 -*-
# @Time   :2021/12/8
# @Author :songtx
# @File   :main.py

import can
from smbus import SMBus
import os
import time
import numpy as np
# from write_mariaDB import write_sql
from dox_enquiry import dox_command
# from io_status import io_status
# from i2c import i2c
# from multiprocessing import Process

os.system('sudo ip link set can0 type can bitrate 250000')
os.system('sudo ifconfig can0 up')


def io_status():
    global var1, var2, data_do, data_di  # 类型为list
    # global edge, l_s_time  # 长按和短按判断，类型为array元素为True/False
    inquiry_do = can.Message(arbitration_id=0x0201, data=[0x01], is_extended_id=False)  # 问询
    bus.send(inquiry_do)                    # 0x0201 DO状态 返回513
    message_do = bus.recv(timeout=1)        # 接收数据
    type_id_do = message_do.arbitration_id  # 返回can报文类型
    if type_id_do == 513:
        # time_float1 = message_do.timestamp          # 浮点数时间戳
        # time_date1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(message_do.timestamp)))  # 转str时间
        # 取回4个字节,预留32通道,bytearray->int->bin(str)->去'0b'->bin->左补32位->反向排列
        data_do = '{:0>32b}'.format(int(bin(int.from_bytes(message_do.data[0:4], "little")).replace('0b', ''), 2))[::-1]
        # print(data_do, type(data_do))
        if var1 != data_do:                 # 差异检测，差异写入数据库
            # write_sql(time_date1, type_id_do, data_do)+++++++++++注意排列顺序有修改
            var1 = data_do                  # 给中间值赋新值
        # else:
        #     time.sleep(0.1)                # 无差异，0.5s扫描一次DI状态

    inquiry_di = can.Message(arbitration_id=0x0301, data=[0x01], is_extended_id=False)
    bus.send(inquiry_di)                    # 0x0301  返回769/0x0301 di状态
    message_di = bus.recv(timeout=1)        # 接收数据
    type_id_di = message_di.arbitration_id  # 返回can报文类型
    if type_id_di == 769:
        # time_float2 = message_di.timestamp          # 浮点数时间戳
        # time_date2 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(message_di.timestamp)))  # 转str时间
        # 取回4个字,预留32通道,bytearray->int->bin(str)->去'0b'->bin->左补32位->反向排列
        data_di = '{:0>32b}'.format(int(bin(int.from_bytes(message_di.data[0:4], "little")).replace('0b', ''), 2))[::-1]
        # print(data_di, type(data_di))
        if data_di != var2:                               # 差异检测，差异写入数据库
            # write_sql(time_date2, type_id_di, data_di)
            for k in range(32):
                if var2[k] == '0' and data_di[k] == '1':  # 32点上升沿检测,没有检测到不复位
                    edge[0, k] = 1                        # 写入边缘检测edge[0]
                    # edge[1, k] = 0                        # 复位下降沿
                    time_diff[0, k] = time.time()         # 把时间写入array[0]，上升沿触发时间戳
                if var2[k] == '1' and data_di[k] == '0':  # 32点下降沿检测,没有检测到不复位
                    edge[1, k] = 1                        # 写入边缘检测edge[1]
                    # edge[0, k] = 0                        # 复位上升沿
                    time_diff[1, j] = time.time()         # 把时间写入array[1]，下降沿触发时间戳
                l_s_time[0, k] = 0 < (time_diff[1, k] - time_diff[0, k]) < 3     # 短脉冲判断
                l_s_time[1, k] = 3 < (time_diff[1, k] - time_diff[0, k]) < 10    # 长脉冲判断
                # print((time_diff[1, k] - time_diff[0, k]))
            var2 = data_di                                    # 给中间值赋新值
            print('上升沿', edge[0])
            print('下降沿', edge[1])
            print('短脉冲', l_s_time[0])
            print('长脉冲', l_s_time[1])
        # else:
        #     time.sleep(0.1)                              # 无差异，0.5s扫描一次DI状态


def i2c():             # list0~list3 数据保持
    global level_5
    for p in range(4):  # 每次执行一次，读取4个当前液位
        level_init[p, 0] = bus_i2c.read_word_data(dict3[p], 0x00)
    level_5 = np.hstack((level_init, level_5))  # 左边插入一组新值
    level_5 = np.delete(level_5, -1, 1)         # 右边抛弃一组旧值
    alarm_high = level_5.mean(axis=1) > level_high
    alarm_low = level_5.mean(axis=1) < level_low
    level = np.vstack((level_5.mean(axis=1), np.vstack((alarm_high, alarm_low)))).T
    return level


def infuse_liquid(liquid_class, err):                        # liquid_class｜液体种类,liquid_class查dict，err为list
    # doxx  dox_06 HC补液泵    dox_07 KCL补液泵    dox_15 pH4补液泵     dox_16 pH6补液泵
    # di信号 di_07 HCL补液按钮  di_08 KCL补液按钮    di_12 pH4补液按钮    di_13 pH6补液按钮
    global dict1, dict_dox_di
    global edge
    global m, l_s_time
    var_level_low, var_level_high = 0, 0
    array2 = i2c()                                           # 读当前液位
    print(array2)
    print('正在执行第', m, '步！')
    # print(edge)
    # print(l_s_time)
    time.sleep(0.2)

    if l_s_time[1, dict_dox_di[str(liquid_class)]] == 1:           # 长脉冲复位补液程序
        print('复位程序～～～～～～～～～～～～～～')
        dox_command([], err)                                 # 复位所有DO指令
        edge[0, dict_dox_di[str(liquid_class)]] = 0                # 复位进入进程的上升沿
        edge[1, dict_dox_di[str(liquid_class)]] = 0                # 复位进入进程的上升沿
        l_s_time[0, dict_dox_di[str(liquid_class)]] = 0
        l_s_time[1, dict_dox_di[str(liquid_class)]] = 0
        return
    if m == 1:                                               # 第一步,主程序条件成立进入本函数，判断启动条件是否满足
        if array2[dict1[str(liquid_class)], 1] == 0.0:       # 高液位为False，条件满足
            var_level_low = array2[dict1[str(liquid_class)], 0]
            dox_command([], err)                             # 启动前复位保护，输出全关指令
            print('当前液位', array2[dict1[str(liquid_class)], 0], '补液条件满足')
            m += 1                                           # 退出本步进，进入下一步
        else:                                                # 不满足条件
            m += 2                                           # 跳转至最后一步
        return
    if m == 2:                                               # 第二步，输出开泵指令
        if True:                                             # 无效判断
            print('开启加药泵')
            dox_command([liquid_class], err)                 # 调用硬件驱动函数，输出开泵指令
            m += 1
        return
    if m == 3:                                               # 第三步，等待液位满
        if array2[dict1[str(liquid_class)], 1] == 1.0:       # 高液位为True
            print('储液罐液位满')
            dox_command([], err)                             # 调用硬件驱动函数，输出关泵指令
            var_level_high = array2[dict1[str(liquid_class)], 0] - var_level_low
            print('补充液体', var_level_high, 'mm')
            edge[0, dict_dox_di[str(liquid_class)]] = 0            # 复位进入进程的上升沿
            edge[1, dict_dox_di[str(liquid_class)]] = 0            # 复位进入进程的上升沿
            l_s_time[0, dict_dox_di[str(liquid_class)]] = 0
            l_s_time[1, dict_dox_di[str(liquid_class)]] = 0
            return
        print(liquid_class, '正在加药++++正在加药++++')
        return


# def logic():
#     global n
#     if n == 1:
#         print('第一步')
#         doxx = dox_command([1], e)         # 写入需要得电的DO通道，顺序任意，不可重复
#         msg = can.Message(arbitration_id=0x0101, data=doxx, is_extended_id=False)
#         bus.send(msg, timeout=1)
#         bus.recv(timeout=1)
#         time.sleep(10)
#         n = n + 1
#         return
#     if n == 2:
#         print('第二步')
#         doxx = dox_command([2])         # 写入需要得电的DO通道，顺序任意，不可重复。空值为全关
#         msg = can.Message(arbitration_id=0x0101, data=doxx, is_extended_id=False)
#         bus.send(msg, timeout=1)
#         bus.recv(timeout=1)
#         time.sleep(10)
#         n = n - 1
#         return
#     if n == 3:
#         print('第三步')
#         doxx = dox_command([2, 3, 13, 16])         # 写入需要得电的DO通道，顺序任意，不可重复
#         msg = can.Message(arbitration_id=0x0101, data=doxx, is_extended_id=False)
#         bus.send(msg, timeout=1)
#         bus.recv(timeout=1)
#         time.sleep(1)
#         n = n + 0
#         return
#     if n == 4:
#         print('第四步')
#         doxx = dox_command([1, 2, 15, 16])         # 写入需要得电的DO通道，顺序任意，不可重复
#         msg = can.Message(arbitration_id=0x0101, data=doxx, is_extended_id=False)
#         bus.send(msg, timeout=1)
#         bus.recv(timeout=1)
#         time.sleep(2)
#         return


if __name__ == "__main__":
    # message_di = 0
    var1, var2 = '00000000000000000000000000000000', '00000000000000000000000000000000'
    data_do, data_di = 0, 0
    edge = np.zeros(64, np.int8).reshape(2, 32)        # DI上升/下降沿检测,edge[0,0]第1个up，edge[1,0]第1个down
    time_diff = np.zeros(64, np.float64).reshape(2, 32)   # DI上升/下降沿的触发时间
    l_s_time = np.zeros(64, np.int8).reshape(2, 32)    # 短按判断l_s_time[0]|长按判断l_s_time[1]
    bus = can.Bus(interface='socketcan', channel='can0', receive_own_messages=eval('False'))
    err_x = [23, 24, 25]                      # 预留的报警输出点
    n = 1
    dict1 = {'6': 0, '7': 1, '15': 2, '16': 3}    # 泵DO对应的液位 liquid_class 转 row
    dict_dox_di = {'6': 6, '7': 7, '15': 11, '16': 12}  # 泵DO对应的DI信号 DI[i -1]

    print('液位初始化')      # 液位初始化程序
    bus_i2c = SMBus(1)      # 创建接口
    level_high = 200    # 高液位报警限值
    level_low = 50      # 低液位报警限值
    level_init = np.zeros(4).reshape(4, 1)   # 创建一个空数组
    level_5 = np.empty(shape=(4, 0))
    dict3 = {0: 0x40, 1: 0x41, 2: 0x42, 3: 0x43}
    for i in range(10):      # 初始化执行一次，预读5个值,修改持值可直接修改预读数量
        for j in range(4):
            level_init[j, 0] = bus_i2c.read_word_data(dict3[j], 0x00)
        level_5 = np.hstack((level_init, level_5))
        time.sleep(0.25)

    while True:
        m = 1                               # 加药程序用的顺控指针，完成一次加药后初始化，4个加药程序共用
        io_status()
        # print(i2c())
        # print('边缘检测||||||||||||', edge)
        # print('长短脉冲，长脉冲判断||||||||||||', l_s_time)
        # print(edge[0, 6], edge[0, 7], edge[0, 11], edge[0, 12])
        # while edge[0, 0] == 1:              # 清洗程序｜DI_01|手指动按钮触发
        #     logic()                      # 最后一步复位上升沿，否则会无法退出

        # if data_di[0: 1] == '0':              # 手动操作 0=手动 1= 自动
        #     print('手动操作模式')
        #     pass
        # if data_di[0: 1] == '1':
        #     print('自动操作模式')
        #     pass

        # while edge[1, 6] == 1:              # 加药程序｜DI_07|清洗液｜HCL｜DO_12|液位0x40
        #     io_status()
        #     infuse_liquid(6, err_x)
        #     time.sleep(1)
        #
        # while edge[1, 7] == 1:              # 加药程序｜DI_08|保护液｜KCL｜DO_13|液位0x41
        #     io_status()
        #     infuse_liquid(7, err_x)
        #     time.sleep(1)
        #
        # while edge[1, 11] == 1:             # 加药程序｜DI_12|缓冲液｜pH4｜DO_14|液位0x42
        #     io_status()
        #     infuse_liquid(15, err_x)
        #
        # while edge[1, 12] == 1:             # 加药程序｜DI_13|缓冲液｜pH6｜DO_15|液位0x43
        #     io_status()
        #     infuse_liquid(16, err_x)

        time.sleep(0.5)
        print('--+--正在执行主循环--+--')   # 可添加其他辅助程序

os.system('sudo ifconfig can0 down')
